package net.guerlab.smart.wx.service.service.impl;

import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.api.impl.WxMpServiceImpl;
import me.chanjar.weixin.mp.config.WxMpConfigStorage;
import net.guerlab.smart.wx.core.enums.WxAppType;
import net.guerlab.smart.wx.core.exception.WxAppInvalidException;
import net.guerlab.smart.wx.mp.spring.storage.WxMpRedisTemplateConfigStorage;
import net.guerlab.smart.wx.service.entity.WxApp;
import net.guerlab.smart.wx.service.service.AfterWxAppDeleteHandler;
import net.guerlab.smart.wx.service.service.AfterWxAppUpdateHandler;
import net.guerlab.smart.wx.service.service.WxAppService;
import net.guerlab.smart.wx.service.service.WxMpManagerService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 微信公众号服务管理实现
 *
 * @author guer
 */
@Service
public class WxMpManagerServiceImpl implements WxMpManagerService, AfterWxAppUpdateHandler, AfterWxAppDeleteHandler {

    private final Lock initLock = new ReentrantLock();

    private WxMpService service;

    private WxAppService wxAppService;

    private RedisTemplate<String, String> redisTemplate;

    @Override
    public WxMpService getService(String appId) {
        appId = StringUtils.trimToNull(appId);
        if (appId == null) {
            return null;
        }

        if (service == null) {
            initLock.lock();
            try {
                Map<String, WxMpConfigStorage> configStorageMap = new ConcurrentHashMap<>(8);
                configStorageMap.put(appId, buildConfigStorage(appId));
                service = new WxMpServiceImpl();
                service.setMultiConfigStorages(configStorageMap, appId);
                return service;
            } finally {
                initLock.unlock();
            }
        }

        if (service.switchover(appId)) {
            return service;
        }

        service.addConfigStorage(appId, buildConfigStorage(appId));
        return service.switchoverTo(appId);
    }

    private WxMpConfigStorage buildConfigStorage(String appId) {
        WxApp wxApp = wxAppService.selectById(appId);
        if (wxApp == null || wxApp.getWxAppType() != WxAppType.MP) {
            throw new WxAppInvalidException();
        }

        WxMpRedisTemplateConfigStorage storage = new WxMpRedisTemplateConfigStorage(redisTemplate);
        storage.setAppId(wxApp.getAppId());
        storage.setSecret(wxApp.getSecret());
        storage.setToken(wxApp.getToken());
        storage.setAesKey(wxApp.getAesKey());
        return storage;
    }

    @Override
    public void afterWxAppUpdateHandler(WxApp wxApp) {
        WxAppType wxAppType = wxApp.getWxAppType();

        if (wxAppType == WxAppType.MP && service != null) {
            service.removeConfigStorage(wxApp.getAppId());
        }
    }

    @Override
    public void afterWxAppDeleteHandler(String appId) {
        if (service != null) {
            service.removeConfigStorage(appId);
        }
    }

    @Autowired
    public void setWxAppService(WxAppService wxAppService) {
        this.wxAppService = wxAppService;
    }

    @Autowired
    public void setRedisTemplate(RedisTemplate<String, String> redisTemplate) {
        this.redisTemplate = redisTemplate;
    }
}
